-----------------------------------------------
-- Project		: ECE 251 FINAL PROJECT
-- Author 		: Mahmut Yilmaz
-- Last Modified: 04/22/2007
-----------------------------------------------

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;
USE work.all;

ENTITY fsm_packetgen_8 IS
	PORT (	-- Global inputs
			gl_ref_clock_crc: IN  STD_LOGIC;	-- T_ref_clock_crc = T_lfsr_clock = 8 * T_ref_clock_eth
			gl_ref_clock_eth: IN  STD_LOGIC;
			gl_enable		: IN  STD_LOGIC;	-- Enable
			gl_start		: IN  STD_LOGIC;	-- Start random packet generation
			gl_n_bytes		: IN  STD_LOGIC_VECTOR (15 DOWNTO 0); -- Number of bytes to be generated
			-- Global outputs
			gl_output		: OUT STD_LOGIC_VECTOR (3 DOWNTO 0);	-- Bit serial output
			---------- INTERNAL SIGNALS -----------------------------
			-- LFSR			
			lfsr_clock		: OUT STD_LOGIC;	-- PosEdge Clock used	
			lfsr_reset		: OUT STD_LOGIC;	-- Resets all flops to 0, active HIGH
      		lfsr_q	 		: IN  STD_LOGIC_VECTOR(7 DOWNTO 0); -- output pseudo-random number
			lfsr_polynomial	: OUT STD_LOGIC_VECTOR(7 DOWNTO 0); -- set LFSR polynomial
			lfsr_seed 		: OUT STD_LOGIC_VECTOR(7 DOWNTO 0); -- seed to LFSR
			lfsr_seed_change: OUT STD_LOGIC;	-- Enable seed change. Resets all flops first.
			lfsr_shift_enable:OUT STD_LOGIC; 	-- Enable shift & pseudo-random number generation, active HIGH
			-- CRC32
			crc32_clock     : OUT STD_LOGIC;
	        crc32_reset   	: OUT STD_LOGIC;
	        crc32_enable  	: OUT STD_LOGIC;
	        crc32_init    	: OUT STD_LOGIC;
	        crc32_data_in 	: OUT STD_LOGIC_VECTOR (7 DOWNTO 0);
	        crc32_crc_out 	: IN  STD_LOGIC_VECTOR (31 DOWNTO 0);
			-- COUNTER
			counter_clock	: OUT STD_LOGIC;
			counter_clear	: OUT STD_LOGIC;
			counter_count	: OUT STD_LOGIC;
			counter_q		: IN  STD_LOGIC_VECTOR(15 DOWNTO 0);
			-- COMPARATOR
			comparator_in0	: OUT STD_LOGIC_VECTOR (15 DOWNTO 0);
			comparator_in1	: OUT STD_LOGIC_VECTOR (15 DOWNTO 0);
      		comparator_q	: IN  STD_LOGIC;
			delayed_comparator_q: IN  STD_LOGIC;
			-- PAR2SER_8
			par2ser8_clock	: OUT STD_LOGIC;	-- PosEdge Clock used	
			par2ser8_reset	: OUT STD_LOGIC;	-- Reset
			par2ser8_enable	: OUT STD_LOGIC;	-- Enable
			par2ser8_input	: OUT STD_LOGIC_VECTOR (7 DOWNTO 0);	-- Input
      		par2ser8_q	 	: IN  STD_LOGIC_VECTOR (3 DOWNTO 0); 	-- Output
			par2ser8_stop_signal: IN  STD_LOGIC;
			-- PAR2SER_32
			par2ser32_clock	: OUT STD_LOGIC;	-- PosEdge Clock used	
			par2ser32_reset	: OUT STD_LOGIC;	-- Reset
			par2ser32_enable: OUT STD_LOGIC;	-- Enable
			par2ser32_input	: OUT STD_LOGIC_VECTOR (31 DOWNTO 0);	-- Input
      		par2ser32_q	 	: IN  STD_LOGIC_VECTOR (3 DOWNTO 0); 	-- Output
			-- MUX
			mux_in0			: OUT STD_LOGIC_VECTOR (3 DOWNTO 0);
			mux_in1			: OUT STD_LOGIC_VECTOR (3 DOWNTO 0);
			mux_sel  		: OUT STD_LOGIC;	-- Multiplexer select
      		mux_q 			: IN  STD_LOGIC_VECTOR (3 DOWNTO 0) 	-- Multiplexer output
			);
END fsm_packetgen_8;

ARCHITECTURE behav OF fsm_packetgen_8 IS
	SIGNAL pre_lfsr_polynomial: STD_LOGIC_VECTOR(7 DOWNTO 0);
	SIGNAL pre_out : STD_LOGIC_VECTOR(3 DOWNTO 0);
BEGIN
	PROCESS (gl_start, gl_ref_clock_crc, gl_ref_clock_eth, gl_enable, pre_lfsr_polynomial,
			par2ser8_q, par2ser32_q, mux_q, counter_q, gl_n_bytes, comparator_q, lfsr_q,
			crc32_crc_out)
	BEGIN
		
		crc32_clock <= (NOT gl_ref_clock_crc)  AND gl_start; -- Add half cycle delay between LFSR and CRC
		counter_clock <= gl_ref_clock_crc AND gl_enable;
		par2ser8_clock <= gl_ref_clock_eth AND gl_enable;
		par2ser32_clock <= gl_ref_clock_eth AND gl_enable;	
		mux_in0 <= 	par2ser8_q;
		mux_in1 <= 	par2ser32_q;
		
		gl_output <= pre_out;	
		pre_out(0) <= mux_q(0) AND 	gl_enable AND gl_start;	
		pre_out(1) <= mux_q(1) AND 	gl_enable AND gl_start;	
		pre_out(2) <= mux_q(2) AND 	gl_enable AND gl_start;	
		pre_out(3) <= mux_q(3) AND 	gl_enable AND gl_start;	
			
		comparator_in0 <= gl_n_bytes;
		comparator_in1 <= counter_q;			

		-- Reset the CRC
		crc32_enable <= gl_start AND delayed_comparator_q; -- gl_start
		crc32_reset  <= NOT gl_start; -- NOT gl_start
		crc32_init <= gl_start;

		-- Reset counter
		counter_clear <= NOT gl_start; -- NOT gl_start
		counter_count <= comparator_q;	

		-- Reset LFSR
		lfsr_shift_enable <= gl_start AND comparator_q; -- gl_start
		lfsr_reset <= NOT gl_enable; -- NOT gl_enable
		lfsr_seed_change <= NOT gl_start; -- NOT gl_start
		lfsr_seed <= x"C1";
		lfsr_polynomial <= pre_lfsr_polynomial;
		lfsr_clock <= gl_ref_clock_crc AND gl_enable;
		
		-- Parallel to serial converters
		par2ser8_enable <= gl_start;
		par2ser8_reset <= (NOT gl_start);	
		par2ser32_enable <= par2ser8_stop_signal;
		par2ser32_reset <= (NOT par2ser8_stop_signal);	
		mux_sel <= par2ser8_stop_signal;	
		
		IF (gl_start='0' AND gl_enable='1') THEN			
			pre_lfsr_polynomial <= x"8E"; -- primitive polynomial to get max efficiency
		ELSE
			IF (gl_ref_clock_crc'EVENT AND gl_ref_clock_crc='0' AND gl_start='1' AND gl_enable='1') THEN
				par2ser8_input <= lfsr_q;
			END IF;
			
			IF (gl_ref_clock_crc'EVENT AND gl_ref_clock_crc='1' AND gl_start='1' AND gl_enable='1') THEN
				-- LFSR output should be ready
				-- Send it to CRC unit
				crc32_data_in <= lfsr_q;
				-- Also, send it to par2ser converter
				par2ser32_input <= crc32_crc_out;
								
				-- Change LFSR polynomial if necessary
				IF lfsr_q = x"C1" THEN
					IF pre_lfsr_polynomial = x"81" THEN
						pre_lfsr_polynomial <= pre_lfsr_polynomial + 1;
					ELSE	
						CASE pre_lfsr_polynomial IS
							WHEN x"8E" => pre_lfsr_polynomial <= x"95";
							WHEN x"95" => pre_lfsr_polynomial <= x"96";
							WHEN x"96" => pre_lfsr_polynomial <= x"A6";
							WHEN x"A6" => pre_lfsr_polynomial <= x"AF";
							WHEN x"AF" => pre_lfsr_polynomial <= x"B1";
							WHEN x"B1" => pre_lfsr_polynomial <= x"B2";
							WHEN x"B2" => pre_lfsr_polynomial <= x"B4";
							WHEN x"B4" => pre_lfsr_polynomial <= x"B8";
							WHEN x"B8" => pre_lfsr_polynomial <= x"C3";
							WHEN x"C3" => pre_lfsr_polynomial <= x"C6";
							WHEN x"C6" => pre_lfsr_polynomial <= x"D4";
							WHEN x"D4" => pre_lfsr_polynomial <= x"E1";
							WHEN x"E1" => pre_lfsr_polynomial <= x"E7";
							WHEN x"E7" => pre_lfsr_polynomial <= x"F3";
							WHEN x"F3" => pre_lfsr_polynomial <= x"FA";
							WHEN x"FA" => pre_lfsr_polynomial <= x"81";
							WHEN OTHERS => pre_lfsr_polynomial <= pre_lfsr_polynomial + 1;
						END CASE;						
					END IF; -- IF pre_lfsr_polynomial = x"81"
				ELSE
					
				END IF; -- IF lfsr_q = x"C1"
			END IF;	-- IF (gl_ref_clock_crc'EVENT AND gl_ref_clock_crc='1')		
		END IF;
	END PROCESS;
END behav;
